package org.ewaf.framework.cookie;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

/**
 * 提供Cookie的统一管理。 一个Cookie里可以存储多个key/value，有利于减少Cookie的数量。
 * 对于经常变动的key/value，应该单独存放在一个Cookie中，否则其它key/value也会被频繁读写。
 */
public class CookieManager {

	private final static Logger log = Logger.getLogger(CookieManager.class);

	/**
	 * 可以通过请求参数传递Cookie
	 */
	public static final String PARAM_COOKIES = "__cookies";

	/**
	 * Cookie编码方式
	 */
	public static final String COOKIE_ENCODING = "UTF-8";

	private final HttpServletRequest request;
	private final String securityCode;
	
	
	public CookieManager(HttpServletRequest request) {
		this(request, null);
	}

	public CookieManager(HttpServletRequest request, String securityCode) {
		this.request = request;
		this.securityCode = securityCode;
	}
   /**
    * request.getAttribute(PARAM_COOKIES)得到的是一个对象，而request.getParameter(PARAM_COOKIES)
    * 得到的肯定是一个字符串
    * @return
    */
   
	
	@SuppressWarnings("unchecked")
	private Map<String, Map<String, String>> getCookies() {
		Map<String, Map<String, String>> cookieMap = (Map<String, Map<String, String>>) request
				.getAttribute(PARAM_COOKIES);
		if (cookieMap != null) {
			return cookieMap;
		}

		String paramCookies = request.getParameter(PARAM_COOKIES);
		cookieMap = new HashMap<String, Map<String, String>>();
		if (StringUtils.isNotBlank(paramCookies)) {
			Map<String, String> oneCookieMap = parseItems(decrypt(paramCookies));
			for (Map.Entry<String, String> e : oneCookieMap.entrySet()) {
				cookieMap.put(e.getKey(), parseItems(e.getValue()));

			}
		} else {
			Cookie[] cookies = request.getCookies();
			if (cookies != null) {
				for (int i = 0; i < cookies.length; i++) {
					Cookie cookie = cookies[i];
					cookieMap.put(cookie.getName(),
							parseItems(decrypt(unescape(cookie.getValue()))));
				}
			}
		}

		request.setAttribute(PARAM_COOKIES, cookieMap);
		return cookieMap;
	}

	/**
	 * 加密
	 */
	private String encrypt(String str) {
		if (str == null) {
			return null;
		}

		if (StringUtils.isBlank(securityCode)) {
			return str;
		}

		byte[] bytes;
		try {
			bytes = str.getBytes("UTF-8");
		} catch (Exception ex) {
			log.error("failed to encrypt cookie value: " + str, ex);
			return null;
		}

		int mask = securityCode.hashCode();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < bytes.length; i++) {
			int v = mask ^ bytes[i];
			sb.append(getChar(v));
			sb.append(getChar(v >>> 4));
		}

		return sb.toString();
	}

	/**
	 * 解密
	 */
	private String decrypt(String str) {
		if (str == null) {
			return null;
		}

		if (StringUtils.isBlank(securityCode)) {
			return str;
		}

		char[] ca = str.toCharArray();
		byte[] bytes = new byte[ca.length / 2];

		int mask = securityCode.hashCode();
		for (int i = 0; i < bytes.length; i++) {
			int v = getInt(ca[i * 2]);
			v |= getInt(ca[i * 2 + 1]) << 4;
			bytes[i] = (byte) (v ^ mask);
		}

		try {
			return new String(bytes, "UTF-8");
		} catch (Exception ex) {
			log.error("failed to decrypt cookie value: " + str, ex);
			return null;
		}
	}

	private char getChar(int val) {
		int part = 0x0F & val;
		if (part < 10) {
			return (char) ('0' + part);
		} else {
			return (char) ('a' + part - 10);
		}
	}

	private int getInt(char c) {
		if (c <= '9') {
			return c - '0';
		} else {
			return c - 'a' + 10;
		}
	}

	private Map<String, String> parseItems(String str) {
		Map<String, String> itemMap = new HashMap<String, String>();
		if (StringUtils.isBlank(str)) {
			return itemMap;
		}

		String[] items = str.split("&");
		for (int i = 0; i < items.length; i++) {
			String item = items[i];
			if (StringUtils.isBlank(item)) {
				continue;
			}

			int offset = item.indexOf('=');
			if (offset > 0) {
				String key = item.substring(0, offset);
				String value = item.substring(offset + 1);

				if (StringUtils.isNotBlank(key)
						&& StringUtils.isNotBlank(value)) {
					itemMap.put(key, unescape(value));
				}
			}
		}

		return itemMap;
	}

	/**
	 * 得到Cookie中的一项值
	 */
	public String getValue(String cookieName, String key) {
		return getValue(cookieName, key, null);
	}

	/**
	 * 得到Cookie中的一项值，若该项不存在，则返回默认值
	 */
	public String getValue(String cookieName, String key, String defaultValue) {
		Map<String, String> itemMap = getCookies().get(cookieName);
		if (itemMap == null) {
			return defaultValue;
		}

		String value = (String) itemMap.get(key);
		return StringUtils.isNotBlank(value) ? value : defaultValue;
	}

	/**
	 * 设置Cookie的一项值
	 */
	public void setValue(String cookieName, String key, String value) {
		Map<String, Map<String, String>> cookieMap = getCookies();

		Map<String, String> itemMap = (Map<String, String>) cookieMap
				.get(cookieName);
		if (itemMap == null) {
			itemMap = new HashMap<String, String>();
			cookieMap.put(cookieName, itemMap);
		}

		if (StringUtils.isNotBlank(value)) {
			itemMap.put(key, value);
		} else {
			itemMap.remove(key);
		}
	}

	private String joinItems(Map<String, String> itemMap) {
		if (itemMap == null) {
			return "";
		}

		StringBuffer sb = new StringBuffer();

		for (Map.Entry<String, String> e : itemMap.entrySet()) {
			String key = (String) e.getKey();
			String value = (String) e.getValue();

			if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
				if (sb.length() > 0) {
					sb.append('&');
				}

				
				
				sb.append(key);
				sb.append('=');
				sb.append(escape(value));
			}
		}

		return sb.toString();
	}

	private void addCookie(HttpServletResponse response, String domain,
			String path, String cookieName, String cookieValue, int age) {

		Cookie cookie = new Cookie(cookieName, escape(encrypt(cookieValue)));
		cookie.setMaxAge(age);

		if (StringUtils.isNotBlank(domain)) {
			cookie.setDomain(domain);
		}

		if (StringUtils.isNotBlank(path)) {
			cookie.setPath(path);
		}

		response.addCookie(cookie);
	}

	/**
	 * 保存Cookie
	 */
	public void saveCookie(HttpServletResponse response, String domain,
			String path, String cookieName, int age) {

		addCookie(response, domain, path, cookieName, joinItems(getCookies()
				.get(cookieName)), age);
	}

	/**
	 * 删除Cookie
	 */
	public void removeCookie(HttpServletResponse response, String domain,
			String path, String cookieName) {

		saveCookie(response, domain, path, cookieName, 0);
	}

	private String escape(String str) {
		if (StringUtils.isNotBlank(str)) {
			try {
				return URLEncoder.encode(str, COOKIE_ENCODING);
			} catch (Exception ex) {
				log.error("failed to encode cookie value: " + str, ex);
			}
		}

		return "";
	}

	private String unescape(String str) {
		if (StringUtils.isNotBlank(str)) {
			try {
				return URLDecoder.decode(str, COOKIE_ENCODING);
			} catch (Exception ex) {
				log.error("failed to decode cookie value: " + str, ex);
			}
		}

		return "";
	}

	/**
	 * 获得HTTP请求参数形式的Cookie
	 */
	public String getCookieParameter() {
		Map<String, String> map = new HashMap<String, String>();
		for (Map.Entry<String, Map<String, String>> e : getCookies().entrySet()) {
			map.put(e.getKey(), joinItems(e.getValue()));
		}

		return encrypt(joinItems(map));
	}
	
}
